{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tDnwEv8FtJm7"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "JlknJBWQtKkI"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "60RdWsg1tETW"
      },
      "source": [
        "# Кастомные слои"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BcJg7Enms86w"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/customization/custom_layers\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ru/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ru/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ru/tutorials/customization/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8a2322303a3f"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UEu3q4jmpKVT"
      },
      "source": [
        "Мы рекомендуем использовать `tf.keras` в качестве высокоуровневого API для построения нейронных сетей. Тем не менее, вы можете использовать большинство API-интерфейсов TensorFlow и они поддерживают быстроые вычисления(eager execution)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Py0m-N6VgQFJ"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TluWFcB_2nP5"
      },
      "outputs": [],
      "source": [
        "print(tf.test.is_gpu_available())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zSFfVVjkrrsI"
      },
      "source": [
        "## Слои: общие установки полезных операций\n",
        "\n",
        "В большинстве случаев при написании кода для моделей машинного обучения вы хотите работать на более высоком уровне абстракции, а не разбираясь с отдельными методами и отдельными переменными.\n",
        "\n",
        "Многие модели машинного обучения можно описать как композицию и последовательное сложение относительно простых слоев. TensorFlow предоставляет как готовые наборы из множества общих слоев, так и простые способы написания собственных слоев для конкретного приложения, либо с нуля, либо в виде создания композиции из существующих слоев.\n",
        "\n",
        "TensorFlow включает полный API [Keras](https://keras.io) в пакете tf.keras, а слои Keras будут очень полезны при построении ваших собственных моделей.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8PyXlPl-4TzQ"
      },
      "outputs": [],
      "source": [
        "# В пакете tf.keras.layers слои являются объектами. Чтобы построить слой,\n",
        "# просто создайте объект класса слоя. Большинство слоев принимают в качестве первого аргумента число\n",
        "# выходной размерности/каналов.\n",
        "layer = tf.keras.layers.Dense(100)\n",
        "# Размерность входных данных часто не нужна, так как ее можно передать\n",
        "# при первом использовании слоя, но вы можете\n",
        "# указать ее вручную, что полезно в некоторых сложных моделях.\n",
        "layer = tf.keras.layers.Dense(10, input_shape=(None, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Fn69xxPO5Psr"
      },
      "source": [
        "Полный список уже существующих слоев можно увидеть в [документации](https://www.tensorflow.org/api_docs/python/tf/keras/layers). Он включает Dense(полносвязанный слой),\n",
        "Conv2D, LSTM, BatchNormalization, Dropout и многие другие."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "E3XKNknP5Mhb"
      },
      "outputs": [],
      "source": [
        "# Для использования слоя просто вызовите его, передав данные\n",
        "layer(tf.zeros([10, 5]))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wt_Nsv-L5t2s"
      },
      "outputs": [],
      "source": [
        "# Слои имеют много полезных методов. Например, вы можете проверить все переменные\n",
        "# в слое вызвав `layer.variables` или просмотреть список обучаемых переменных с помощью\n",
        "# `layer.trainable_variables`. В нашем случае полносвязанный слой\n",
        "# будут иметь переменные для весов и смещений.\n",
        "layer.variables"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6ilvKjz8_4MQ"
      },
      "outputs": [],
      "source": [
        "# Переменные также доступны через приятные акцессоры\n",
        "layer.kernel, layer.bias"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O0kDbE54-5VS"
      },
      "source": [
        "## Реализация пользовательских слоев\n",
        "\n",
        "Лучший способ реализовать свой собственный слой - это расширить класс tf.keras.Layer и реализовать:\n",
        "\n",
        "1. `__init__`, где вы можете выполнять инициализацию, независимую от входных данных\n",
        "2. `build`, где вы знаете размерность входных тензоров и можете выполнить остальную часть инициализации.\n",
        "3. `call`, где вы выполняете прямое вычисление.\n",
        "\n",
        "Обратите внимание, что вам не нужно ждать вызова `build` для создания ваших переменных, вы можете создать их прямо в методе `__init__`. Однако преимущество их создания в `build` заключается в том, что вы создаете переменные на основании входных данных, с которыми будет работать слой. С другой стороны, создание переменных в `__init__` будет означать, что размерность входных данных, необходимая для создания переменных, должна быть явно указана."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5Byl3n1k5kIy"
      },
      "outputs": [],
      "source": [
        "class MyDenseLayer(tf.keras.layers.Layer):\n",
        "  def __init__(self, num_outputs):\n",
        "    super(MyDenseLayer, self).__init__()\n",
        "    self.num_outputs = num_outputs\n",
        "\n",
        "  def build(self, input_shape):\n",
        "    self.kernel = self.add_weight(\"kernel\",\n",
        "                                  shape=[int(input_shape[-1]),\n",
        "                                         self.num_outputs])\n",
        "\n",
        "  def call(self, input):\n",
        "    return tf.matmul(input, self.kernel)\n",
        "\n",
        "layer = MyDenseLayer(10)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vrmBsYGOnuGO"
      },
      "outputs": [],
      "source": [
        "_ = layer(tf.zeros([10, 5])) # Вызывается метод layer.builds а затем call"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1bsLjiPfnvat"
      },
      "outputs": [],
      "source": [
        "print([var.name for var in layer.trainable_variables])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tk8E2vY0-z4Z"
      },
      "source": [
        "Overall code is easier to read and maintain if it uses standard layers whenever possible, as other readers will be familiar with the behavior of standard layers. If you want to use a layer which is not present in `tf.keras.layers`, consider filing a [github issue](http://github.com/tensorflow/tensorflow/issues/new) or, even better, sending us a pull request!\n",
        "\n",
        "Любой код легче читать и поддерживать, если он по возможности использует стандартные слои, так как другие читатели наверняка будут знакомы с поведением стандартных слоев. Если вы хотите использовать слой, которого нет в `tf.keras.layers`, подумайте о том, чтобы создать [багрепорт на github](http://github.com/tensorflow/tensorflow/issues/new) или, что еще лучше, отправьте нам запрос на добавление вашего слоя!"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Qhg4KlbKrs3G"
      },
      "source": [
        "## Модели: Составление слоев\n",
        "\n",
        "Многие интересные вещи, например сложные слои, в моделях машинного обучения реализованы путем компоновки существующих слоев. Например, каждый остаточный блок в сети ResNet представляет собой комбинацию сверток, пакетных нормализаций и ярлыка. Слои могут быть вложены в другие слои.\n",
        "\n",
        "Обычно, если нам нужны такие методы модели, как:` Model.fit`, `Model.evaluate` и` Model.save` мы наследуемся от `keras.Model`,  (более подробно см. [Пользовательские слои и модели Keras](../../guide/keras/custom_layers_and_models.ipynb)).\n",
        "\n",
        "Еще одна особенность, предоставляемая `keras.Model` и чего нет в `keras.layers.Layer`, заключается в том, что в дополнение к отслеживанию переменных, `keras.Model` также отслеживает свои внутренние слои, что упрощает их проверку.\n",
        "\n",
        "Например, вот блок ResNet:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "N30DTXiRASlb"
      },
      "outputs": [],
      "source": [
        "class ResnetIdentityBlock(tf.keras.Model):\n",
        "  def __init__(self, kernel_size, filters):\n",
        "    super(ResnetIdentityBlock, self).__init__(name='')\n",
        "    filters1, filters2, filters3 = filters\n",
        "\n",
        "    self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))\n",
        "    self.bn2a = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')\n",
        "    self.bn2b = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))\n",
        "    self.bn2c = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "  def call(self, input_tensor, training=False):\n",
        "    x = self.conv2a(input_tensor)\n",
        "    x = self.bn2a(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2b(x)\n",
        "    x = self.bn2b(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2c(x)\n",
        "    x = self.bn2c(x, training=training)\n",
        "\n",
        "    x += input_tensor\n",
        "    return tf.nn.relu(x)\n",
        "\n",
        "\n",
        "block = ResnetIdentityBlock(1, [1, 2, 3])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7D8ZR5mqtokj"
      },
      "outputs": [],
      "source": [
        "_ = block(tf.zeros([1, 2, 3, 3])) "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MJ8rzFpdoE_m"
      },
      "outputs": [],
      "source": [
        "block.layers"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dewldLuDvQRM"
      },
      "outputs": [],
      "source": [
        "len(block.variables)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FrqIXeSetaYi"
      },
      "outputs": [],
      "source": [
        "block.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wYfucVw65PMj"
      },
      "source": [
        "Часто модели, состоящие из множества слоев, просто вызывают один слой за другим. Это можно сделать очень просто, используя `tf.keras.Sequential`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "L9frk7Ur4uvJ"
      },
      "outputs": [],
      "source": [
        "my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1),\n",
        "                                                    input_shape=(\n",
        "                                                        None, None, 3)),\n",
        "                             tf.keras.layers.BatchNormalization(),\n",
        "                             tf.keras.layers.Conv2D(2, 1,\n",
        "                                                    padding='same'),\n",
        "                             tf.keras.layers.BatchNormalization(),\n",
        "                             tf.keras.layers.Conv2D(3, (1, 1)),\n",
        "                             tf.keras.layers.BatchNormalization()])\n",
        "my_seq(tf.zeros([1, 2, 3, 3]))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tVAsbFITuScB"
      },
      "outputs": [],
      "source": [
        "my_seq.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c5YwYcnuK-wc"
      },
      "source": [
        "# Что дальше\n",
        "\n",
        "Теперь вы можете вернуться к предыдущему руководству и адаптировать пример линейной регрессии с использованием слоев и модели."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "custom_layers.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
